'''
knn算法用于回归---波士顿房价回归问题

摘要：以波士顿房价数据集为例，使用 kNN 模型解决回归问题——预测房价。
'''

from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.neighbors import KNeighborsRegressor
from sklearn.datasets import fetch_california_housing

# 1. 获取波士顿房价原始数据
# import pandas as pd
# import numpy as np
# data_url = "http://lib.stat.cmu.edu/datasets/boston"
# raw_df = pd.read_csv(data_url, sep="\s+", skiprows=22, header=None)
# data = np.hstack([raw_df.values[::2, :], raw_df.values[1::2, :2]])
# target = raw_df.values[1::2, 2]

# Ames房价数据集---埃姆斯（英語：Ames），美国爱荷华州中部的一座城市
# from sklearn.datasets import fetch_openml
# ames_housing = fetch_openml(name="house_prices", as_frame=True)
# print('Ames房价数据集：', ames_housing)

# 加州房价数据集
#
ca_housing = fetch_california_housing()
print('加州房价数据：', ca_housing.keys())
print('feature_names：', ca_housing.feature_names)

# 拆分数据集并训练模型
'''
与分类任务一样，在回归任务中，我们也要使用训练集来训练模型，并使用验证集来验证模型的性能。
'''
# 将岩本特征和售价赋值给项x,y
x, y = ca_housing.data, ca_housing.target
# x, y = data, target # boston
# x, y = ames_housing.data, ames_housing.target
x_train, x_test, y_train, y_test = train_test_split(x, y)

print('查看拆分的结果 x_train：', x_train.shape)
print('查看拆分的结果 x_test：', x_test.shape)
print('查看拆分的结果 y_train：', y_train.shape)
print('查看拆分的结果 y_test：', y_test.shape)

# 创建一个实例，参数保持默认设置----回归
knn_reg = KNeighborsRegressor()
# 拟合训练集数据
knn_reg.fit(x_train, y_train)
# 查看模型在训练和验证集的性能表现
print('训练集准确率：%.3f' % knn_reg.score(x_train, y_train))  # 0.452
print('验证集准确率：%.3f' % knn_reg.score(x_test, y_test))  # 0.131

# 结果训练集和验证集分数都很低，说明模型出现了欠拟合的问题。我们需要对数据进行处理，或者对模型进行调优
# - 欠拟合（under-fitting）:训练集和验证集得分都很低

print('================网格搜索找最优参数=================')

n_neighbors = tuple(range(1, 21, 1))
cv_reg = GridSearchCV(estimator=KNeighborsRegressor(),
                      param_grid={'n_neighbors': n_neighbors},
                      cv=5)

cv_reg.fit(x, y)

print('最优参数best_params_：', cv_reg.best_params_)
print('最优得分best_score_：', cv_reg.best_score_)

print('================使用最优参数查看训练和验证集的准确率=================')
best_n_neighbors = cv_reg.best_params_['n_neighbors']
print('best_n_neighbors：', best_n_neighbors)
knn_reg_best = KNeighborsRegressor(n_neighbors=best_n_neighbors)
knn_reg_best.fit(x_train, y_train)
print('训练集准确率：%.3f' % knn_reg_best.score(x_train, y_train))
print('验证集准确率：%.3f' % knn_reg_best.score(x_test, y_test))

'''
1. 代入当使用网格搜索的最优参数时，得到的准确率结果反而降低了（书中是提高了的），我用例boston,california, amse的数据都是一样的结果?
2. 网格搜索方法的结果cv_reg.best_score_与手动传如最佳参数的训练集和验证集准确率区别时什么，书中没有说明？
'''

# 导出cvs
# if __name__ == '__main__':
#     df = pd.DataFrame(ca_housing.data, columns=ca_housing.feature_names)
#     df['MEDV'] = ca_housing['target']
#     df.to_csv('./ca_housing.csv', index=None)
